{--
    Math functions from 
    'http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html java.lang.Math'.
    Many of those functions are overloaded for 'Float' and 'Double', so we 
    introduce a type class 'Floating' to avoid overloading issues.

    For example
    > f x = 2 * sqrt x
    would give a type error, because the compiler wouldn't know whether to use 
    > sqrt :: Float  -> Double
    or
    > sqrt :: Double -> Double

    Note that many functions return 'Double', no matter if the argument was 'Double' or
    'Float'.
-}

protected package frege.prelude.Math where

import frege.prelude.PreludeBase(Double, Float, Int, Long, IO, Num, Real)
import Java.lang.Math as J public()

--- Returns a 'Double' with a positive sign, greater than or equal to 0.0 and less than 1.0.
public         native random    java.lang.Math.random   :: () -> IO Double

infixr 15  `**`

(**) = pow

--- square
sqr :: Num a => a -> a
sqr x = x * x

class  Real r ⇒ Floating r where
    --- The value that is closer than any other to @pi@, the ratio of the circumference of a circle to its diameter.
    pi                  ∷ r

    --- The value that is closer than any other to @e@, the base of the natural logarithms.
    e                   ∷ r

    --- Returns the arc sine of a value; the returned angle is in the range -'pi'/2 through 'pi'/2.
    asin                ∷ r → Double

    --- Returns the arc cosine of a value; the returned angle is in the range 0.0 through 'pi'.
    acos                ∷ r → Double

    --- Returns the arc tangent of a value; the returned angle is in the range -'pi'/2 through 'pi'/2.
    atan                ∷ r → Double

    --- Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta).
    atan2               ∷ r → r → Double

    --- Returns the cube root of a value.
    cbrt                ∷ r → Double

    --- Returns the smallest (closest to negative infinity) value that is greater than or equal to the argument and is equal to a mathematical integer.
    ceil                ∷ r → Double

    --- Returns the first argument with the sign of the second argument.
    copySign            ∷ r → r → r

    --- Returns the trigonometric cosine of an angle.
    cos                 ∷ r → Double

    --- Returns the hyperbolic cosine of a floating point value.
    cosh                ∷ r → Double

    --- Returns Euler's number 'e' raised to the power of a floating-point value. 
    exp                 ∷ r → Double

    --- Returns @e^x -1@.
    expm1               ∷ r → Double

    {--
      Returns the largest (closest to positive infinity) value that is
      less than or equal to the argument and is equal to a mathematical integer.
    
      Special cases:
    
        - If the argument value is already equal to a mathematical integer,
        then the result is the same as the argument.
        - If the argument is NaN or an infinity or positive zero or
        negative zero, then the result is the same as the argument.

    -}
    floor               ∷ r -> Double

    --- Returns the unbiased exponent used in the representation of a floating point number.
    getExponent         ∷ r → Int

    --- Returns 'sqrt' @(x² + y²)@ without intermediate overflow or underflow.
    hypot               ∷ r → r → Double

    --- Computes the remainder operation on two arguments as prescribed by the IEEE 754 standard.
    ieeeRemainder       ∷ r → r → Double

    --- Returns the natural logarithm (base 'e') of a value.
    log                 ∷ r → Double

    --- Returns the base 10 logarithm of a value.
    log10               ∷ r → Double

    --- Returns the natural logarithm of the sum of the argument and 1.
    log1p               ∷ r → Double
    
    --- Returns the logarithm of the second argument to the base given by the first argument. 
    logBase             ∷ r → r → Double

    --- Returns the floating-point number adjacent to the first argument in the direction of the second argument.
    nextAfter           ∷ r → r → Double

    --- Returns the floating-point value adjacent to the argument in the direction of positive infinity.
    nextUp              ∷ r → r

    --- Returns the value of the first argument raised to the power of the second argument.
    pow                 ∷ r → r → Double

    --- Returns the double value that is closest in value to the argument and is equal to a mathematical integer.
    rint                ∷ r → Double

    --- Returns the closest 'Long' to the argument, with ties rounding up.
    round               ∷ r → Long

    --- > scalb d scaleFactor
    --- Return @d * 2^scaleFactor@ rounded as if performed by a single correctly rounded floating-point multiply.
    scalb               ∷ r → Int → r

    --- Returns the signum function of the argument; zero if the argument is zero, 1.0 if the argument is greater than zero, -1.0 if the argument is less than zero.
    signum              ∷ r → r

    --- Returns the trigonometric sine of an angle.
    sin                 ∷ r → Double

    --- Returns the hyperbolic sine of a value.
    sinh                ∷ r → Double

    --- Returns the correctly rounded positive square root of a value.
    sqrt                ∷ r → Double

    --- Returns the trigonometric tangent of an angle.
    tan                 ∷ r → Double

    --- Returns the hyperbolic tangent of a floating point value.
    tanh                ∷ r → Double

    --- Converts an angle measured in radians to an approximately equivalent angle measured in degrees.
    toDegrees           ∷ r → Double

    --- Converts an angle measured in degrees to an approximately equivalent angle measured in radians.
    toRadians           ∷ r → Double

    --- Returns the size of an ulp of the argument.
    ulp                 ∷ r → r

    --- Inverse hyperbolic function for 'sinh'
    asinh               ∷ r → Double

    --- Inverse hyperbolic function for 'cosh'
    acosh               ∷ r → Double

    --- Inverse hyperbolic function for 'tanh'
    atanh               ∷ r → Double

    logBase x y         =  log y / log x

instance Floating Double where
    acos = J.acos
    asin = J.asin
    atan = J.atan
    atan2 = J.atan2
    cbrt = J.cbrt
    ceil = J.ceil
    copySign = J.copySign
    cos  = J.cos
    cosh = J.cosh
    e    = J.e
    exp  = J.exp
    expm1 = J.expm1
    floor = J.floor
    getExponent = J.getExponent
    hypot = J.hypot
    ieeeRemainder = J.ieeeRemainder
    log  = J.log
    log10 = J.log10
    log1p = J.log1p
    nextAfter = J.nextAfter
    nextUp = J.nextUp
    pi = J.pi
    asinh x = log (x + sqrt (1.0 + x*x))
    acosh x = log (x + (x + 1.0) * sqrt ((x - 1.0)/(x + 1.0)))
    atanh x = 0.5 * log ((1.0 + x) / (1.0 - x))
    pow = (J.**)
    rint = J.rint
    round = J.round
    scalb = J.scalb
    signum = J.signum
    sin  = J.sin
    sinh = J.sinh
    sqrt = J.sqrt
    tan  = J.tan    
    tanh = J.tanh
    toDegrees = J.toDegrees
    toRadians = J.toRadians
    ulp = J.ulp

instance Floating Float where
    acos = J.acos
    asin = J.asin
    atan = J.atan
    atan2 = J.atan2
    cbrt = J.cbrt
    ceil = J.ceil
    copySign = J.copySign
    cos  = J.cos
    cosh = J.cosh
    e    = J.e.float
    exp  = J.exp
    expm1 = J.expm1
    floor = J.floor
    getExponent = J.getExponent
    hypot = J.hypot
    ieeeRemainder = J.ieeeRemainder
    log  = J.log
    log10 = J.log10
    log1p = J.log1p
    nextAfter = J.nextAfter
    nextUp = J.nextUp
    pi = J.pi.float
    asinh y = log (x + sqrt (1.0 + x*x)) where x = y.double
    acosh y = log (x + (x + 1.0) * sqrt ((x - 1.0)/(x + 1.0)))  where x = y.double
    atanh x = 0.5 * log ((1.0 + x) / (1.0 - x))
    pow = (J.**)
    rint = J.rint
    round = J.round
    scalb = J.scalb
    signum = J.signum
    sin  = J.sin
    sinh = J.sinh
    sqrt = J.sqrt
    tan  = J.tan 
    tanh = J.tanh
    toDegrees = J.toDegrees
    toRadians = J.toRadians
    ulp = J.ulp
